using System;
using System.Collections.Generic;
using DslCrossModelFramework.Environment;
using DslCrossModelFramework.VSPackage;
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Shell;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;

namespace DslCrossModelFramework.Environment {
    public class VisualStudioEnvironmentConfiguration : IEnvironmentConfiguration, IDisposable {

        private Guid dslCrossModelOutputWindowPane = new Guid("{7BEC9B11-C226-4e10-A319-7DA3C34D9888}");

        public VisualStudioEnvironmentConfiguration() {
            // Register for solution events which will reset the environment
            DTE dte = DSLCrossModelFrameworkPackage.GetGlobalService(typeof (DTE)) as DTE;
            if(dte != null) {
                
                solutionEvents = dte.Events.SolutionEvents;
                solutionEvents.AfterClosing += new _dispSolutionEvents_AfterClosingEventHandler(solutionEvents_AfterClosing);
                solutionEvents.ProjectAdded += new _dispSolutionEvents_ProjectAddedEventHandler(solutionEvents_ProjectAdded);
                solutionEvents.ProjectRenamed += new _dispSolutionEvents_ProjectRenamedEventHandler(solutionEvents_ProjectRenamed);
                solutionEvents.ProjectRemoved += new _dispSolutionEvents_ProjectRemovedEventHandler(solutionEvents_ProjectRemoved);
                solutionEvents.Renamed += new _dispSolutionEvents_RenamedEventHandler(solutionEvents_Renamed);


                windowEvents = dte.Events.get_WindowEvents(null);
                windowEvents.WindowActivated += new _dispWindowEvents_WindowActivatedEventHandler(windowEvents_WindowActivated);
                windowEvents.WindowClosing += new _dispWindowEvents_WindowClosingEventHandler(windowEvents_WindowClosing);
                windowEvents.WindowCreated += new _dispWindowEvents_WindowCreatedEventHandler(windowEvents_WindowCreated);
                windowEvents.WindowMoved += new _dispWindowEvents_WindowMovedEventHandler(windowEvents_WindowMoved);

                 //subscribe to document events
                //IVsRunningDocumentTable rdt =
                //    (IVsRunningDocumentTable)
                //    DSLCrossModelFrameworkPackage.GetGlobalService(typeof (SVsRunningDocumentTable));

                //rdt.AdviseRunningDocTableEvents(this, out dwCookie);

            }
            //sharedStore.RuleManager.SuspendRuleNotification();
        }

        //private uint dwCookie;

        //private Transaction transaction;

        #region Window Events

        void windowEvents_WindowMoved(Window Window, int Top, int Left, int Width, int Height) {

        }

        void windowEvents_WindowCreated(Window Window) {

        }

        void windowEvents_WindowClosing(Window Window) {

        }

        void windowEvents_WindowActivated(Window GotFocus, Window LostFocus) {

        }

        #endregion

        #region Solution Events

        void solutionEvents_Renamed(string OldName) {
            ResetEnvironment();
        }

        void solutionEvents_ProjectRemoved(Project Project) {
            ResetEnvironment();
        }

        void solutionEvents_ProjectRenamed(Project Project, string OldName) {
            ResetEnvironment();
        }

        void solutionEvents_ProjectAdded(Project Project) {
            ResetEnvironment();
        }

        void solutionEvents_AfterClosing() {
            ResetEnvironment();
        }

        #endregion

        private SolutionEvents solutionEvents;
        private WindowEvents windowEvents;
        private List<Store> dynamicStores = new List<Store>();
        private Store sharedStore = new Store();

        public List<Store> Stores {
            get { List<Store> stores = new List<Store>();
                    stores.Add(sharedStore);
                    dynamicStores.ForEach(delegate(Store currentDynamicStore) {
                                              if(currentDynamicStore != null) {
                                                  stores.Add(currentDynamicStore);
                                              }
                                          });
                return stores;
            }
        }

        public void LogMessage(string message) {
            WriteMessage(message, OutputMessageType.Information);
        }

        public event EventHandler<ModelFileEventArgs> ModelFileAdded;

        public event EventHandler<ModelFileEventArgs> ModelFileRemoved;

        public event EventHandler<ModelFileEventArgs> ModelFileLoaded;

        public event EventHandler<ModelFileEventArgs> ModelFileUnloaded;
        public event EventHandler<ModelFileEventArgs> ModelFileUpdated;

        public void InitializeEnvironment() {
            GetModelFiles(GetExportablePackages());
            isInitialized = true;
        }

        public bool IsInitialized {
            get { return isInitialized; }
        }
    
        private bool isInitialized;

        private ModelDirectory modelDirectory;
        
        public string Name {
            get { return "Visual Studio"; }
        }

        public bool IsActive {
            get { return DSLCrossModelFrameworkPackage.GetGlobalService(typeof(DTE)) != null; }
        }

        public List<IModelFileInfo> ModelFiles {
            get { return modelFiles; }
        }

        public List<DslToolsPackageInfo> Packages {
            get { return packages; }
        }

        public void ResetEnvironment() {
            isInitialized = false;
            this.modelDirectory = null;
            this.dynamicStores.Clear();
            this.sharedStore = new Store();
            this.modelFiles.Clear();
        }

        private List<DslToolsPackageInfo> GetExportablePackages() {
            if(packages == null)
            using( ModelFileHelper modelFileHelper = new ModelFileHelper()) {
                packages = modelFileHelper.GetExportablePackages();
            }
            return packages;
        }

        private List<DslToolsPackageInfo> packages = null;

        private void GetModelFiles(IEnumerable<DslToolsPackageInfo> packageInfo) {
            using (ModelFileHelper modelFileHelper = new ModelFileHelper()) {
                modelFileHelper.ModelLoadingNotification += new EventHandler<ModelLoadingNotificationEventArgs>(modelFileHelper_ModelLoadingNotification);
                foreach (IModelFile foundFile in modelFileHelper.GetDslModels(packageInfo)) {
                    bool alreadyAdded = false;
                    foreach (IModelFileInfo modelFile in modelFiles) {
                        if (modelFile.ModelFile.Equals(foundFile.UniqueModelFileReference)) {
                            alreadyAdded = true;
                        }
                    }
                    if(!alreadyAdded) {
                        DslToolsPackageInfo package = null;
                        foreach (DslToolsPackageInfo info in packageInfo) {
                            if(info.Guid == foundFile.DslPackageId) {
                                package = info;
                            }
                        }
                        IModelFileInfo fileInfo = new ModelFileInfo(foundFile, package, this.GetModelDirectory() as ModelDirectory, sharedStore);
                        fileInfo.Loaded += new EventHandler<ModelFileEventArgs>(fileInfo_Loaded);
                        fileInfo.Loading += new EventHandler<ModelFileEventArgs>(fileInfo_Loading);
                        fileInfo.Unloading += new EventHandler<ModelFileEventArgs>(fileInfo_Unloading);
                        fileInfo.Unloaded += new EventHandler<ModelFileEventArgs>(fileInfo_Unloaded);
                        modelFiles.Add(fileInfo);
                        if(ModelFileAdded != null){ModelFileAdded(this, new ModelFileEventArgs(fileInfo));}
                        if(foundFile is IDynamicStoreModelFile) {
                            ((IDynamicStoreModelFile)foundFile).DynamicStoreFound += new System.EventHandler<DynamicModelFileEventArgs>(VisualStudioEnvironmentConfiguration_DynamicStoreFound);
                            ((IDynamicStoreModelFile)foundFile).StoreDisposed += new System.EventHandler<DynamicModelFileEventArgs>(VisualStudioEnvironmentConfiguration_StoreDisposed);
                        }
                    }
                }
                modelFileHelper_ModelLoadingNotification(this, new ModelLoadingNotificationEventArgs(100));
            }
            
        }

        private StatusBar statusBar;

        void modelFileHelper_ModelLoadingNotification(object sender, ModelLoadingNotificationEventArgs e) {
            DTE dte = DSLCrossModelFrameworkPackage.GetGlobalService(typeof (DTE)) as DTE;
            if(dte != null) {
                if (e.PercentageComplete < 100) {
                    if(statusBar == null) {
                        statusBar = dte.StatusBar;
                        statusBar.Animate(true, vsStatusAnimation.vsStatusAnimationFind);
                    }
                    
                    statusBar.Text = "Searching for models...";
                    statusBar.Progress(true, "Searching for models...", e.PercentageComplete, 100);
                    
                }
                else {
                    if(statusBar != null) {
                        statusBar.Animate(false, vsStatusAnimation.vsStatusAnimationFind);
                        statusBar.Progress(false, "", 100, 100);
                        statusBar.Clear();
                    }
                    statusBar = null;
                }
            }
        }


        void fileInfo_Unloaded(object sender, ModelFileEventArgs e) {
            if (ModelFileUnloaded != null) { ModelFileUnloaded(this, e); }
            LogMessage(string.Format("Unloaded Model File: {0}", e.FileInfo.ModelFile.UniqueModelFileReference));
        }

        void fileInfo_Loaded(object sender, ModelFileEventArgs e) {
            if (ModelFileLoaded != null) { ModelFileLoaded(this, e); }
            LogMessage(string.Format("Loaded Model File: {0}", e.FileInfo.ModelFile.UniqueModelFileReference));
        }

        void fileInfo_Unloading(object sender, ModelFileEventArgs e) {
            LogMessage(string.Format("Unloading Model File: {0}", e.FileInfo.ModelFile.UniqueModelFileReference));
        }

        void fileInfo_Loading(object sender, ModelFileEventArgs e) {
            LogMessage(string.Format("Loading Model File: {0}", e.FileInfo.ModelFile.UniqueModelFileReference));
        }

        void VisualStudioEnvironmentConfiguration_StoreDisposed(object sender, DynamicModelFileEventArgs e) {
            foreach(IModelFileInfo modelFileInfo in modelFiles) {
                if (modelFileInfo.ModelFile.Equals(e.ModelFile)) {
                    foreach(Store store in dynamicStores) {
                        if(store.Id == e.Store.Id) {
                            dynamicStores.Remove(store);
                            break;
                        }
                    }
                    modelFileInfo.MakeShared(sharedStore);
                }
            }

        }

        void VisualStudioEnvironmentConfiguration_DynamicStoreFound(object sender, DynamicModelFileEventArgs e) {
            foreach (IModelFileInfo modelFileInfo in modelFiles) {
                if (modelFileInfo.ModelFile.Equals(((IModelFile)sender))) {
                    modelFileInfo.MakeDynamic(e.Store);
                }
            }
        }

        private List<IModelFileInfo> modelFiles = new List<IModelFileInfo>();
        


        public IModelFileHelper GetTempModelFileHelper() {
            return new ModelFileHelper();
        }

        public IModelDirectory GetModelDirectory() {
            if(modelDirectory == null) {
                modelDirectory = new ModelDirectory(new ModelFileHelper());
            }
            return modelDirectory;
        }

        /// <summary>
        /// Checks for dynamic stores.
        /// </summary>
        public void CheckForDynamicStores() {

            // Get the running documents
            List<Guid> openStores = new List<Guid>();
            DTE dte = DSLCrossModelFrameworkPackage.GetGlobalService(typeof(DTE)) as DTE;

            if (dte == null) {
                return;
            }

            IVsWindowFrame windowFrame;

            System.IServiceProvider serviceProvider = new ServiceProvider(dte as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); // Use your global service provider object
            foreach (ModelFileInfo file in ModelFiles) {
                if (file.ModelFile is IDynamicStoreModelFile) {
                    string fullPathToDocument = ((IDynamicStoreModelFile)file.ModelFile).LocalFilePath; // Use whatever full path you have to the document

                    if(string.IsNullOrEmpty(fullPathToDocument)) {
                        continue;
                    }

                    IVsUIHierarchy hier;
                    uint itemid;
                    // Ask VS if the document is open
                    Microsoft.VisualStudio.Shell.VsShellUtilities.IsDocumentOpen(serviceProvider, fullPathToDocument,
                                                                                 Guid.Empty, out hier,
                                                                                 out itemid, out windowFrame);

                    if (windowFrame != null) {
                        object docView;

                        windowFrame.GetProperty((int) __VSFPROPID.VSFPROPID_DocView, out docView);

                        ModelingDocView modelingDocView = docView as ModelingDocView;

                        if (modelingDocView != null) {
                            openStores.Add(modelingDocView.DocData.Store.Id);

                            // There is a Dsl Modelling File open
                            foreach (DslToolsPackageInfo packageInfo in packages) {
                                // The open document is in the model file list so register the store
                                ((IDynamicStoreModelFile)file.ModelFile).RegisterDynamicStore(modelingDocView.DocData.Store);
                                break;
                            }
                        }
                    }
                }
            }

            // Make sure that the dynamic files are still dynamic
            foreach(IModelFileInfo fileInfo in ModelFiles) {
                if(fileInfo.IsDynamic) {
                    bool isStillDynamic = false;
                    foreach(Guid storeId in openStores) {
                        if(fileInfo.Store.Id == storeId) {
                            isStillDynamic = true;
                            break;
                        }
                    }
                    if(!isStillDynamic) {
                        fileInfo.MakeShared(sharedStore);
                    }
                }
            }


            foreach(ModelFileInfo file in ModelFiles) {
                if(file.ModelFile.IsDirty) {
                    if(ModelFileUpdated != null) {ModelFileUpdated(this, new ModelFileEventArgs(file));}
                }
            }
        }

        /// <summary>
        /// The type of a message which will be returned to the client
        /// </summary>
        public enum OutputMessageType {
            /// <summary>
            /// Information only
            /// </summary>
            Information,
            /// <summary>
            /// Describes a warning
            /// </summary>
            Warning,
            /// <summary>
            /// Describes an exception
            /// </summary>
            Exception
        }

        /// <summary>
        /// Writes a message to the attached client.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="messageType">Type of the message.</param>
        public void WriteMessage(string message, OutputMessageType messageType) {
            if (messageType == OutputMessageType.Information) {

                IVsOutputWindowPane outputWindowPane = GetOutputPane();
                outputWindowPane.OutputString(string.Format("Dsl Integration: {0}{1}", message, System.Environment.NewLine));
            }
        }

        private IVsOutputWindowPane CreateOutputPane() {
            IVsOutputWindow outputWindow;
            DTE dte = DSLCrossModelFrameworkPackage.GetGlobalService(typeof(DTE)) as DTE;
            System.IServiceProvider serviceProvider = new ServiceProvider(dte as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); // Use your global service provider object
            outputWindow = serviceProvider.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;

            outputWindow.CreatePane(ref dslCrossModelOutputWindowPane, "Dsl Cross Model Integration", 1, 1);

            return VsShellUtilities.GetOutputWindowPane(serviceProvider, dslCrossModelOutputWindowPane);
        }

        private IVsOutputWindowPane GetOutputPane() {
            
            IVsOutputWindowPane outputWindowPane;

            DTE dte = DSLCrossModelFrameworkPackage.GetGlobalService(typeof(DTE)) as DTE;
            System.IServiceProvider serviceProvider = new ServiceProvider(dte as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); // Use your global service provider object
            outputWindowPane = VsShellUtilities.GetOutputWindowPane(serviceProvider, dslCrossModelOutputWindowPane);            
            if(outputWindowPane == null) {
                outputWindowPane = CreateOutputPane();
            }

            return outputWindowPane;
        }


        //#region IVsRunningDocTableEvents Members

        //int IVsRunningDocTableEvents.OnAfterAttributeChange(uint docCookie, uint grfAttribs) {
        //    return VSConstants.S_OK;
        //}

        //int IVsRunningDocTableEvents.OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) {
        //    return VSConstants.S_OK;
        //}

        //int IVsRunningDocTableEvents.OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) {
        //    return VSConstants.S_OK;
        //}

        //int IVsRunningDocTableEvents.OnAfterSave(uint docCookie) {
        //    return VSConstants.S_OK;
        //}

        //int IVsRunningDocTableEvents.OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) {
        //    return VSConstants.S_OK;
        //}

        //int IVsRunningDocTableEvents.OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) {
        //    return VSConstants.S_OK;
        //}

        //#endregion

        #region IDisposable Members

        public void Dispose() {
            //IVsRunningDocumentTable rdt = (IVsRunningDocumentTable)DSLCrossModelFrameworkPackage.GetGlobalService(typeof(SVsRunningDocumentTable));
            //rdt.UnadviseRunningDocTableEvents(dwCookie);
        }

        #endregion
    }


}